package me.dwk.dandan.function.reflect;

import java.lang.reflect.Modifier;
import java.util.Arrays;

import lombok.extern.slf4j.Slf4j;

/**
 * filter class when visiting the search path with {@link ClassFinder}
 *
 * @since 3.0
 */
@Slf4j
public class ExtendsClassFilter {

	private final Class<?>[]	parents;																// parent
																										// classes
																										// to check
	private final boolean		inner;																	// are inner
																										// classes
																										// OK?

	private final ClassLoader	contextClassLoader	= Thread.currentThread().getContextClassLoader();	// Potentially
																										// expensive;
																										// do it
																										// once

	ExtendsClassFilter(	Class<?>[] parents,
						boolean inner) {
		this.parents = parents;
		this.inner = inner;
	}

	public boolean accept(String className) {
		if (!className.contains("$") || inner)
			return isChildOf(parents, className, contextClassLoader);
		return false;
	}

	/**
	 * @param parentClasses
	 *            list of classes to check for
	 * @param strClassName
	 *            name of class to be checked
	 * @param contextClassLoader
	 *            the classloader to use
	 * @return true if the class is a non-abstract, non-interface instance of at
	 *         least one of the parent classes
	 */
	private boolean isChildOf(
								Class<?>[] parentClasses,
								String strClassName,
								ClassLoader contextClassLoader) {
		try {
			Class<?> targetClass = Class.forName(strClassName, false, contextClassLoader);

			if (!targetClass.isInterface()
				&& !Modifier.isAbstract(targetClass.getModifiers())) {
				boolean anyMatch = Arrays.stream(parentClasses).anyMatch(parent -> parent.isAssignableFrom(targetClass));
				return anyMatch;
			}
		} catch (UnsupportedClassVersionError | ClassNotFoundException | NoClassDefFoundError | VerifyError e) {
			log.debug(e.getLocalizedMessage(), e);
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "ExtendsClassFilter [parents="
					+
					(parents != null ? Arrays.toString(parents) : "null")
					+ ", inner="
					+ inner
					+ "]";
	}

}
